package com.nika.usercenter.service.impl;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.nika.usercenter.utils.AlgorithmUtils;
import common.ErrorCode;
import com.nika.usercenter.constant.UserConstant;
import com.nika.usercenter.exception.BusinessException;
import com.nika.usercenter.model.domain.User;
import com.nika.usercenter.service.UserService;
import com.nika.usercenter.mapper.UserMapper;
import com.nika.usercenter.utils.IDUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import static com.nika.usercenter.constant.UserConstant.USER_LOGIN_STATE;

//todo 修改为自定义异常
/**
 * @author HT
 * 用户服务实现类
 * @createDate 2022-08-09 23:10:27
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    /**
     * 盐值混淆密码
     */
    private static final String SALT = "nika";
    /**
     * 状态键
     */

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {
        //校验非空
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"参数有为为空");
        }

        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"账户密码小于8位");
        }
//        账户不能包含特殊字符,长度在6-18
        String regex = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,18}$";
        if (!userAccount.matches(regex)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"输入账号需要在6-18位且是数字和字母的组合且不能有特殊符号");
        }

        //密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"两次输入密码不一致");
        }

        String md5HexPassword = DigestUtils.md5Hex(SALT + userPassword);

        //账户不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        long count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"已经有相同的注册账户");
        }
        //加密

        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(md5HexPassword);
        boolean saveResult = this.save(user);
        if (!saveResult) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"没有成功保存");
        }

        return user.getId();
    }

    @Override
    public User doLoginBySession(String userAccount, String userPassword, HttpServletRequest request) {

        User safetyUser = checkLoginUserMessage(userAccount, userPassword);
        //记录用户的登录态

        request.getSession().setAttribute(USER_LOGIN_STATE,safetyUser);
        return safetyUser;
    }

    @Override
    public String doLoginByToken(String userAccount, String userPassword) {
        User user = checkLoginUserMessage(userAccount, userPassword);
        String tokenId = "user-token:" + IDUtils.generate();
        redisTemplate.opsForValue().set(tokenId,user.getId(),1, TimeUnit.DAYS);
        return tokenId;
    }

    private User checkLoginUserMessage(String userAccount, String userPassword){
        //校验非空
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"账户长度小于4");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"账户密码小于8");
        }
        //账户不能包含特殊字符,长度在6-18
        String regex = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,18}$";
        if (!userAccount.matches(regex)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"输入账号需要在6-18位且是数字和字母的组合且不能有特殊符号");
        }

        String md5HexPassword = DigestUtils.md5Hex(SALT + userPassword);

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword",md5HexPassword);
        User user = userMapper.selectOne(queryWrapper);

        if(user == null){
            log.info("user login failed, userAccount cannot match password");
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码不正确");
        }
        //用户脱敏
        return getSafetyUser(user);
    }

    /**
     * 用户脱敏
     * @param originUser
     * @return
     */
    @Override
    public User getSafetyUser(User originUser){
        if(originUser == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"传入的源user为null");
        }
        User safetyUser = new User();
        safetyUser.setId(originUser.getId());
        safetyUser.setUsername(originUser.getUsername());
        safetyUser.setUserAccount(originUser.getUserAccount());
        safetyUser.setAvatarUrl(originUser.getAvatarUrl());
        safetyUser.setGender(originUser.getGender());
        safetyUser.setPhone(originUser.getPhone());
        safetyUser.setUserRole(originUser.getUserRole());
        safetyUser.setEmail(originUser.getEmail());
        safetyUser.setUserStatus(originUser.getUserStatus());
        safetyUser.setCreateTime(originUser.getCreateTime());
        safetyUser.setTags(originUser.getTags());
        safetyUser.setProfile(originUser.getProfile());
        return safetyUser;
    }

    /**
     * 用户注销
     * @param request
     */
    @Override
    public int userLogOut(HttpServletRequest request) {
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return 1;
    }

    /**
     * 根据标签搜索用户
     * @param tagNameList
     * @return
     */
    @Override
    public List<User> searchUsersByTags(List<String> tagNameList) {

        if(CollectionUtils.isEmpty(tagNameList)){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }

//        方法一:利用sql
        long startTime = System.currentTimeMillis();
        if(CollectionUtils.isEmpty(tagNameList)){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        for (String tagName : tagNameList) {
            queryWrapper = queryWrapper.like("tags",tagName);
        }
        List<User> userList = userMapper.selectList(queryWrapper);
        log.info("sql time:" + (System.currentTimeMillis()-startTime));
        return userList.stream().map(this::getSafetyUser).collect(Collectors.toList());
    }

    public List<User> searchUsersByTagsInMemory(List<String> tagNameList) {

        long startTime2 = System.currentTimeMillis();
        List<User> users = userMapper.selectList(new QueryWrapper<>());
        List<User> resUserList = new ArrayList<>();
        Gson gson=new Gson();
        resUserList = users.stream().filter(user -> {
            String tagStr = user.getTags();
            if(StringUtils.isEmpty(tagStr)){
                return false;
            }
            Set<String> tempTagNameSet = gson.fromJson(tagStr, new TypeToken<Set<String>>() {
            }.getType());
            for (String tagName : tagNameList) {
                if(StringUtils.isEmpty(tagName)){
                    continue;
                }
                if(!tempTagNameSet.contains(tagName)) {
                    return false;
                }
            }
            return true;
        }).map(this::getSafetyUser).collect(Collectors.toList());
        log.info("storage time:" + (System.currentTimeMillis()-startTime2));
        return resUserList;
    }

    @Override
    public int updateUser(User user, User loginUser) {
        //先判空
        if(user == null || loginUser == null){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        //查找要更新的用户是否存在
        long id = user.getId();
        if(id<0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //不是管理员 并且 不是更新自己的信息
        if(!isAdmin(loginUser) && user.getId() != loginUser.getId()){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        User oldUser = userMapper.selectById(id);
        if(oldUser == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        return userMapper.updateById(user);
    }

    @Override
    public int updateUserByToken(User user, User loginUser) {
        //先判空
        if(user == null || loginUser == null){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR);
        }
        //查找要更新的用户是否存在
        long id = user.getId();
        if(id<0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //不是管理员 并且 不是更新自己的信息
        if(!isAdmin(loginUser) && user.getId() != loginUser.getId()){
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        User oldUser = userMapper.selectById(id);
        if(oldUser == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        return userMapper.updateById(user);
    }



    @Override
    public boolean isAdmin(HttpServletRequest request){
        User userObj = (User)request.getSession().getAttribute(USER_LOGIN_STATE);
        return userObj != null && userObj.getUserRole()== UserConstant.ADMIN_ROLE;
    }

    @Override
    public boolean isAdmin(User loginUser){
        return loginUser != null && loginUser.getUserRole()== UserConstant.ADMIN_ROLE;
    }

    /**
     * session
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        if(request == null){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"request 为空");
        }
        Object loginUser = request.getSession().getAttribute(USER_LOGIN_STATE);
        return (User)loginUser;
    }
    /**
     * token
     * @param request
     * @return
     */

    @Override
    public User getLoginUserByToken(HttpServletRequest request) {
        if(request == null){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"request 为空");
        }
        String token = request.getHeader("token");
        if(StringUtils.isEmpty(token)){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"请求头中没有携带token");
        }
        Object userId = redisTemplate.opsForValue().get(token);
        if(userId == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"无法获得user对象");
        }
        User loginUser = userMapper.selectById((int) userId);
        if(loginUser == null){
            throw new BusinessException(ErrorCode.NO_AUTH,"未登录");
        }
        return getSafetyUser(loginUser);
    }

    @Override
    public Object getRecommendFromCache(long pageSize, long pageNum, HttpServletRequest request) {
        if(pageNum<0 || pageSize<0){
            pageNum = 1;
            pageSize = 10;
        }
        ValueOperations<Object,Object> redisOperations = redisTemplate.opsForValue();

        String token = request.getHeader("token");
        Integer userId = (Integer)redisTemplate.opsForValue().get(token);
        String redisKey = String.format("nika-usercenter-userId:%s",userId);
        Object userPage = redisOperations.get(redisKey);
        if(userPage != null){
            return userPage;
        }
        userPage = page(new Page<>(pageNum, pageSize), new QueryWrapper<>());

        try {
            redisOperations.set(redisKey,userPage,30000,TimeUnit.MILLISECONDS);

        } catch (Exception e) {
            log.error("redisKey set error",e);
        }

        return userPage;
    }
    @Override
    public List<User> matchUsers(long num, User loginUser) {
        String currentUserTags = loginUser.getTags();

        Gson gson = new Gson();
        List<String> currentUserTagList = gson.fromJson(currentUserTags, new TypeToken<List<String>>() {
        }.getType());
        if(StringUtils.isEmpty(currentUserTags)){
            throw new BusinessException(ErrorCode.PARAMS_NULL_ERROR,"当前用户没有标签用来");
        }
        List<User> userList = this.list();
        SortedMap<Integer,Float> userMatchMap = new TreeMap<>();

        for (int i = 0; i < userList.size(); i++) {
            String userTags = userList.get(i).getTags();
            if(StringUtils.isEmpty(userTags)){
                continue;
            }
            List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {
            }.getType());
            float score = AlgorithmUtils.levenshtein(currentUserTagList, userTagList);
            userMatchMap.put(i,score);
        }
        return null;
    }


}




